package file

import (
	"fmt"
	"gopatch/file"
	"gopatch/global"
	"io"
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// FindAllFiles 传统遍历文件
func FindAllFiles(allFileMap *map[string][]string, path string) {
	log.Println("开始遍历所有文件...")
	var counter int64

	// projectPath := cmd.Property.BaseProjectPath

	err := filepath.Walk(path, func(path string, info fs.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			// 是文件，获取文件名，放入 map
			name := info.Name()
			// 获取扩展名
			ext := filepath.Ext(name)
			// 去除扩展名
			filenameWithoutExt := name[:len(name)-len(ext)]

			// (*allFileMap)[name] = append((*allFileMap)[filenameWithoutExt],
			//	strings.Replace(path, targetPath, "", 1))

			(*allFileMap)[filenameWithoutExt] = append((*allFileMap)[filenameWithoutExt], path)
			atomic.AddInt64(&counter, 1)
		}
		return nil
	})
	if err != nil {
		panic("遍历targetPath报错!" + err.Error())
	}

	log.Printf("所有文件遍历结束...共有文件%d\n", counter)

	// for key, val := range *allFileMap {
	//	log.Printf("key--%s, val--%v\n", key, val)
	// }
}

// FindAllFilesGo 对外的协程遍历文件，封装同步工具
func FindAllFilesGo(path string) map[string][]file.FileStruct {

	// allFileMap-所有文件的Map集合，key-去除扩展名的文件名，val-所有同名（去除扩展名）的文件结构体集合
	var allFileMapT sync.Map
	// 文件对应锁 Map
	var fileLockMap sync.Map
	var wg sync.WaitGroup

	_, err := os.Stat(path)
	if err != nil {
		panic(fmt.Errorf(`遍历文件路径不正确 %s : %v`, path, err.Error()))
	}

	log.Printf("开始启用协程遍历 %s 所有文件...\n", filepath.Base(path))

	findAllFilesGo(&allFileMapT, &fileLockMap, path, &wg)

	wg.Wait()
	log.Println("所有文件遍历结束....")

	// 最终的所有文件Map集合
	var finalAllFileMap map[string][]file.FileStruct = make(map[string][]file.FileStruct)

	counter := 0
	allFileMapT.Range(func(filenameWithoutExt, val any) bool {
		list := val.(*[]file.FileStruct)

		finalAllFileMap[filenameWithoutExt.(string)] = *list

		counter += len(*list)

		return true
	})

	log.Printf("所有文件遍历结束...共有文件%d\n", counter)

	return finalAllFileMap
}

// findAllFilesGo 使用协程加快遍历文件速度
func findAllFilesGo(allFileMap *sync.Map, fileLockMap *sync.Map, path string, wg *sync.WaitGroup) {

	wg.Add(1)
	defer func() {
		// 在 Done 之前稍等一会
		/**
		因为递归过程中可能出现下面的 go 协程已注册但未执行到 wg.Add ，然而外面的已经执行完并 wg.Done
		导致最外面的 wg.Wait 被放行，此时出现两种错误；
		1. 文件遍历数不正确
		2. WaitGroup is reused before previous Wait has returned
		*/
		time.Sleep(100 * time.Millisecond)
		wg.Done()
	}()

	dir, _ := os.ReadDir(path)
	for _, entry := range dir {
		if entry.IsDir() {
			go findAllFilesGo(allFileMap, fileLockMap, filepath.Join(path, entry.Name()), wg)
		} else {
			// 是文件，获取文件名，放入 map
			name := entry.Name()
			// 获取扩展名
			ext := filepath.Ext(name)
			// 去除扩展名
			filenameWithoutExt := name[:len(name)-len(ext)]
			// 绝对路径
			absFilePath := filepath.Join(path, name)

			if len(filenameWithoutExt) == 0 {
				// 去除扩展名后文件名为空，直接使用文件名
				filenameWithoutExt = name
			}

			// stat, err := os.Stat(absFilePath)
			// if err != nil {
			//	global.AppendMsg(global.ERROR, fmt.Sprintf("获取 %s 文件状态时报错,err:%v", absFilePath, err))
			//	continue
			// }

			fileT := file.FileStruct{
				AbsFilePath: absFilePath,
			}

			// 是文件，先获取对应的锁
			tempMx := sync.Mutex{}
			mx, _ := fileLockMap.LoadOrStore(filenameWithoutExt, &tempMx)
			mutex := mx.(*sync.Mutex)

			// 在文件的 Map 中 LoadOrStore
			var tempStrings []file.FileStruct
			mutex.Lock()
			actualFileList, _ := allFileMap.LoadOrStore(filenameWithoutExt, &tempStrings)
			files := actualFileList.(*[]file.FileStruct)

			*files = append(*files, fileT)
			mutex.Unlock()
		}
	}

}

// CopyFile 复制文件到指定目录
func CopyFile(srcPath string, patchPath string) {
	src, err := os.Open(srcPath)
	if err != nil {
		panic("复制文件出错!" + err.Error())
	}
	defer func(src *os.File) {
		err := src.Close()
		if err != nil {
		}
	}(src)
	// 去除包根目录
	newSubfix := strings.Replace(srcPath, global.GetProperty().TargetPath, global.GetProperty().PatchName, 1)
	newPath := filepath.Join(patchPath, newSubfix)
	dir := filepath.Dir(newPath)
	if err = os.MkdirAll(dir, 0755); err != nil {
		panic("复制文件创建文件夹出错!" + err.Error())
	}

	dest, err := os.Create(newPath)
	if err != nil {
		panic("复制文件出错!" + err.Error())
	}
	defer func(dest *os.File) {
		err := dest.Close()
		if err != nil {
		}
	}(dest)
	_, err = io.Copy(dest, src)
	if err != nil {
		panic("复制文件出错!" + err.Error())
	}
}
